#include "journal.h"
#include "plugins.h"
#include "io.h"
#include "simple.h"
#include "settings.h"
#include "journ_wzrd.h"
#include "crypt.h"
#include "error.h"
#include "gui_io.h"
#include "login.h"
#include "dict.h"

struct elog_journ_state **_journals = NULL;
struct elog_journ_state *_current_state = NULL;

struct elog_journ_state *elog_journ_state_new()
{
	struct elog_journ_state *nue = malloc(sizeof *nue);
	nue->settings = malloc(sizeof *(nue->settings));
	nue->name = NULL;
	nue->currentFile = NULL;
	nue->key = NULL;
	nue->settings->path = NULL;
	nue->settings->encryption = NULL;
	return nue;
}

void elog_journ_state_free(struct elog_journ_state *state)
{
	if (state->name != NULL)
		free(state->name);
	if (state->currentFile != NULL)
		free(state->currentFile);
	if (state->key != NULL)
		free(state->key);
	if (state->settings->encryption != NULL)
		free(state->settings->encryption);
	if (state->settings->path != NULL)
		free(state->settings->path);
	free(state->settings);
	free(state);
}



int elog_journ_initialize(const char *fileName)
{
	struct elog_io_file *f =
	    elog_io_initialize(fileName, ELOG_IO_READ);
	if (f == NULL) {
		create_dialog();
		return 0;
	}
	unsigned int len;
	unsigned char *t_1 = elog_io_readRestofFile(f, &len);
	char *meta = elog_sp_toChar(t_1, len);


	int n_journals = 0;	//reading how many journals we have.
	char *tmp = NULL;
	elog_xml_scanf(meta, "<journals>.<how many>.%s", &tmp);
	char *tmp_2 = elog_sp_whiteSpaceRemove(tmp);
	n_journals = elog_sp_stringToShort(tmp_2);
	free(tmp_2);
	free(tmp);
	
	if (n_journals == 0) {
		elog_err_print_console("No Journals apparently\n");
		return 2;
	}

	_journals = malloc((sizeof *_journals) * (n_journals + 1));	//allocating journals, plus a terminator
	int c;

	for (c = 0; c < n_journals; ++c) {
		_journals[c] = elog_journ_state_new();
		char *tag;
		char *n = elog_sp_shortToString(c);
		elog_sp_cat(&tag, "<journals>.<journal num=", n, ">.%s", NULL);	//making our tag
		free(n);

		char *journal = NULL;
		elog_xml_scanf(meta, tag, &journal);
		if (journal == NULL) {
			elog_err_print
			    ("Malformed journal configuration, missing tag\n");
			_journals = NULL;
			return 4;
		}
		elog_xml_scanf(journal, "<name>.%s",
			       &(tmp));
		_journals[c]->name = elog_sp_whiteSpaceRemove(tmp);
		free(tmp);
		//we have to do this whiteSpaceRemove because xml_scanf used to
		//but it doesn't anymore, and it won't hurt any of the
		//data we store here...
		elog_xml_scanf(journal, "<settings>.<path>.%s",
			       &(tmp));
		_journals[c]->settings->path = elog_sp_whiteSpaceRemove(tmp);
		free(tmp);
		
		elog_xml_scanf(journal, "<settings>.<encryption>.%s",
			       &(tmp));
		_journals[c]->settings->encryption = elog_sp_whiteSpaceRemove(tmp);
		free(tmp);
		
		if (_journals[c]->name == NULL
		    || _journals[c]->settings->path == NULL
		    || _journals[c]->settings->encryption == NULL) {
			elog_err_print
			    ("Malformed journal configuration, missing a tag\n");
			_journals = NULL;
			return 3;
		}

		free(journal);
		free(tag);
	}
	_journals[n_journals] = NULL;	//terminator
	_current_state = _journals[n_journals - 1];
	free(meta);
	elog_io_close(f);
	return 0;
}
void elog_journ_saveStates(const char *fileName)
{
	if (_journals == NULL)
		return;

	char *xml = elog_xml_start("<journals>", 64 * 1024);
	int c;
	for (c = 0; _journals[c] != NULL; ++c) {
		char *tag;
		char *n = elog_sp_shortToString(c);
		elog_sp_cat(&tag, "<journal num=", n, ">", NULL);	//making our tag

		elog_xml_printf(xml, "<journals>.%s", tag, "");
		free(tag);
		elog_sp_cat(&tag, "<journals>.<journal num=", n, ">.%s",
			    NULL);

		elog_xml_printf(xml, tag, "<name>", _journals[c]->name);

		elog_xml_printf(xml, tag, "<settings>", "");
		free(tag);
		elog_sp_cat(&tag, "<journals>.<journal num=", n,
			    ">.<settings>.%s", NULL);
		elog_xml_printf(xml, tag, "<path>",
				_journals[c]->settings->path);

		elog_xml_printf(xml, tag, "<encryption>",
				_journals[c]->settings->encryption);
		free(tag);
		free(n);
	}
	char *howmany = elog_sp_shortToString(c);
	elog_xml_printf(xml, "<journals>.%s", "<how many>", howmany);
	free(howmany);

	struct elog_io_file *f =
	    elog_io_initialize(fileName, ELOG_IO_WRITE);
	elog_io_writeLine(f, xml, 0);
	elog_io_close(f);
	free(xml);
}

void elog_journ_save()
{
	if (_current_state != NULL && _journals != NULL) {
		free(_current_state->settings->path);
		free(_current_state->settings->encryption);
		elog_sp_cat(&(_current_state->settings->path),
			    elog_set_get_str("path"), NULL);
		elog_sp_cat(&(_current_state->settings->encryption),
			    elog_set_get_str("encryption_method"), NULL);
		_current_state->key = viewKey();
		struct dict *current = NULL;
		elog_set_currentFile(NULL, &current);
		if (current != NULL) {
			if (_current_state->currentFile != NULL)
				free(_current_state->currentFile);

			elog_sp_cat(&(_current_state->currentFile),
				    dict_get(current, "fileName"), NULL);
		}
	}
}
void _journ_search_function(void *in)
{
	elog_gui_io_search(in);
}

int elog_journ_load(const char *journ)
{
	int res = elog_journ_load_no_last_file(journ);
	if (res)
		 return res;

    struct cal_date *date = elog_gui_al_calendarDate(NULL);
	char *year = elog_sp_shortToString(date->year);
	char *month = elog_sp_shortToString(date->month);
	char *day = elog_sp_shortToString(date->day);
	free(date);

	char *path;
	elog_sp_cat(&path, elog_set_get_str("path"), "/", year, "/", month,
		    "/", day, "/", NULL);
	elog_gui_io_markCalendar();
	elog_gui_io_dispLocation(path);
	//traverSearch(_current_state->settings->path, NULL, _journ_search_function);	//Null to use  last term

	if (_current_state->currentFile != NULL)
		elog_gui_io_open(_current_state->currentFile, NULL);
	else
		elog_gui_io_newEntry(path);

	char *title;
	elog_sp_cat(&title, _ELOG_NAME, "::", _current_state->name, NULL);
	elog_gui_al_win_title(title);
	free(title);

	return 0;
}

void elog_journ_fork(const char *name)
{
	if (_current_state == NULL)
		return;
	int last;
	for (last = 0; _journals[last] != NULL; ++last);

	struct elog_journ_state *nue = elog_journ_state_new();

	elog_sp_cat(&(nue->name), name, NULL);
	nue->key = malloc(256);
	memcpy(nue->key, _current_state->key, 256);
	elog_sp_cat(&(nue->currentFile), _current_state->currentFile,
		    NULL);
	elog_sp_cat(&(nue->settings->path), _current_state->settings->path,
		    NULL);
	elog_sp_cat(&(nue->settings->encryption),
		    _current_state->settings->encryption, NULL);
	_journals[last] = nue;
	_journals[last + 1] = NULL;
}
struct elog_journ_state *elog_journ_current()
{
	return _current_state;
}
int elog_journ_set(const char *path)
{
	if (_journals == NULL)
		return 2;

	int c;
	for (c = 0; _journals[c] != NULL; ++c) {
		if (strcmp(_journals[c]->settings->path, path) == 0) {
			_current_state = _journals[c];
			return 0;
		}
	}

	return 1;
}
int elog_journ_exists(const char *path)
{
	if (!_journals)
		return 0;
	int c;
	for (c = 0; _journals[c] != NULL; ++c)
		if (strcmp(_journals[c]->settings->path, path) == 0)
			return 1;

	return 0;
}

const char *elog_journ_in_current(const char *path)
{
	//First check if it's the current journal.
	int c;
	for (c = 0; _current_state->settings->path[c]; ++c)
		if (_current_state->settings->path[c] != path[c])
			break;

	if (!(_current_state->settings->path[c]))
		return NULL;

	int journ = 0;		//current journ evaluating
	/* We will start on the first journ
	 *  Compare each char and move to the next when it's wrong.
	 */
	while (_journals[journ]) {
		int good = 1;
		int c;
		for (c = 0; _journals[journ]->settings->path[c]; ++c) {
			if (_journals[journ]->settings->path[c] != path[c]) {
				++journ;
				good = 0;
				break;
			}
		}
		if (good)
			return _journals[journ]->name;
	}
	return NULL;
}

char **elog_journ_list()
{
	if (_journals == NULL)
		return NULL;
	char **list = malloc((sizeof *list) * 256);
	int c;
	for (c = 0; _journals[c] != NULL; ++c)
		list[c] = _journals[c]->name;
	list[c] = NULL;
	return list;
}

void elog_journ_new()
{
	struct elog_journ_state *nue = elog_journ_state_new();
	_current_state = nue;


	int size = 0;
	if (_journals != NULL)
		for (; _journals[size] != NULL; ++size);

	struct elog_journ_state **array =
	    malloc((sizeof *array) * (size + 2));

	int c;
	for (c = 0; c < size; ++c)
		array[c] = _journals[c];
	array[c] = nue;
	array[c + 1] = NULL;
	free(_journals);
	_journals = array;
}

const char *elog_journ_path(const char *name)
{
	if (_journals) {
		int c;
		for (c = 0; _journals[c] != NULL; ++c)
			if (strcmp(name, _journals[c]->name) == 0)
				break;
		if (_journals[c])
			return _journals[c]->settings->path;
	}
	return NULL;
}

int elog_journ_load_no_last_file(const char *journ)
{
	 if (_journals == NULL) {
        elog_err_print_console
			 ("Multiple Journals not initialized\n");
        return 1;
	 }
	 int c;
	 for (c = 0; _journals[c] != NULL; ++c)
		  if (strcmp(_journals[c]->name, journ) == 0)
			   break;
	 if (_journals[c] == NULL) {
		  elog_err_print_console("Failed to find journal\n");
		  return 1;
	 }
	 struct elog_journ_state *old = _current_state;
	 _current_state = _journals[c];
	 elog_set_set_str("encryption_method",
					  _journals[c]->settings->encryption);
	 elog_set_set_str("path", _journals[c]->settings->path);
	 if (_current_state->settings->encryption[0] != 'N') //if we are encrypting
	 {

		  if (_current_state->key == NULL) {
			   if (elog_gui_al_setKey(ELOG_LGN_MODE_SWITCH)) {
					//Backout!
					_current_state = old;
					elog_set_set_str("path",
                         _current_state->settings->
									 path);
					elog_set_set_str("encryption_method",
                         _current_state->settings->
									 encryption);
					return 1;
			   }

			   _current_state->key = viewKey();
		  } else
			   setKey(_current_state->key);
		  //user inputs a key.

	 } else
		  setKey(NULL);
	 /*The program depends on a NULL key if the correct one isn't set */

	 /*Calling plugins */
	 
	 struct dict *plgn = dict_new();
	 {
	 	dict_set(plgn, "state", _current_state);
	 	dict_set_type(plgn, "state", DICT_TYPE_OBJ);
	 }
	 elog_plgn_call_all("___journChange", plgn);
	 dict_unref(plgn);
	 //TODO: this won't do to be called after the fact.


	 char *title;
	 elog_sp_cat(&title, _ELOG_NAME, "::", _current_state->name, NULL);
	 elog_gui_al_win_title(title);
	 free(title);

	 return 0;

}

